"""
 Module: plotutils.py

 Ver 0.8.0
 Original Release: 01/02/2009

 Rev 0.9.2; Last: 03/12/2013 - Previous: 03/11/2013 (AEM)
   - Reconfigure, fuonctionalize
   - 

 Handle everything having to do with manipulating the gnuPlot template
 and log files.

 This file is part of the DMM Reader project.

 Copyright (C) 2012-2013  Arnold E. Morgan

    DMM Reader is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""

# *** System Modules ***
import os
import sys
import subprocess
import time

def read_template(PlotPath, TemplateFName):
   """ Get template values with substitution markers for the appropriate
   measurement type under test.
   """

   TemplatePath = os.path.join(PlotPath, TemplateFName)
   RtnVals = (False, "", "")
   RtnMssg = ""
   try:
      TemplateFile = open(TemplatePath).read()
      RtnVals = (True, TemplateFile, "")
   except IOError, (errno, strerror):
      RtnMssg = "\nTemplate file open/read error(%s): %s\n" % (errno, strerror)
      RtnVals = (False, "", RtnMssg)
   except:
      RtnMssg = "\nUnexpected template file read error: %s\n" % sys.exc_info()
      RtnVals = (False, "", RtnMssg)
   #finally: 
   #   print "Config file settings read complete"

   return RtnVals

# *************** End of fcn read_template ************

def read_log(ParentPath, LogFName):
   """ Read in log file values so start/end X-axis
       and Min/Max Y-axis values can be determined.
   """
   
   LogPath = os.path.join(ParentPath, LogFName)

   RtnVals = (False, "", "")
   RtnMssg = ""
   try:
      LogFile = open(LogPath).read()
      RtnVals = (True, LogFile, "")
   except IOError, (errno, strerror):
      RtnMssg = "\nLog file %s open/read error(%s): %s\n" % (LogPath, errno, strerror)
      RtnVals = (False, "", RtnMssg)
   except:
      RtnMssg = "\nUnexpected %s log file read error: %s\n" % (LogPath, sys.exc_info())
      RtnVals = (False, "", RtnMssg)
   #finally: 
   #   print "Config file settings read complete"

   return RtnVals
# *************** End of fcn read_template ************

def find_x_range(SessionLog):
   """ X axis start and end values = first and last
   date/time records from log file.
   """

   SessionLogList = SessionLog.split('\n')
   TempStartLine = SessionLogList[0].strip()
   TempStartFields = TempStartLine.split(' ')
   Start_X = TempStartFields[1] + ' ' + TempStartFields[2]

   # Because of the split on '\n' the last
   # record will be blank. Last **value** is
   # 2nd from the end.
   TempEndLine = SessionLogList[-2].strip()
   TempEndFields = TempEndLine.split(' ')
   End_X = TempEndFields[1] + ' ' + TempEndFields[2]

   return (Start_X, End_X)
# *************** End of fcn find_x_range ************

def get_plot_cols(PlotType):
   """ Determine which Y data column(s) are to be plotted. 

   Assume temperature measurements have 2 values, degrees C and F.
   All other measurement types will have jut 1 value.
   Return the column numbers as a dictionary.
   """

   DataCol = {}
   if PlotType == "DMM":
     DataCol['DMM'] = 4       # V,O,A is in col 4
   else:
      DataCol['TempC'] = 4   # Deg C is in col 4
      DataCol['TempF'] = 5   # Deg F is in col 5

   return DataCol

def find_y_range(SessionLog, DataCol):
   """ Read thru the log file and find the min and max
       y axis values.

   This function should be general enough to be independent of
   measurement type.
   Return a 3 value tuple:
    - Min Y value
    - Max Y value
    - Any necessary messaging
   """
   
   SessionLogList = SessionLog.split('\n')
   ListCol = DataCol - 1
   RtnMssg = ""
   SkippedRows = 0
   Min_Y = 0
   Max_Y = 0
   for Line in SessionLogList:
      TempLine = Line.strip()
      TempFields = TempLine.split(' ')

      MyIntFloat = 0
      IsInt = True
      # 1st column should always be the integer counter.
      # If not, skip this row.
      # There will always be a blank row at the end of the log file.
      try:
         MyIntFloat = int(float(TempFields[0]))
      except ValueError:
         IsInt = False
         SkippedRows += 1

      # If the first colun is not an integer, then something is wrong.
      # skip this row altogether.
      if IsInt:
         if MyIntFloat == 1:
            # Initialize to first row values. Smallest or largest may not
            # be 0.
            Min_Y = float(TempFields[ListCol])
            Max_Y = float(TempFields[ListCol])
         else:
            if float(TempFields[ListCol]) < Min_Y:  # Needs error checking
               Min_Y = float(TempFields[ListCol])

            if float(TempFields[ListCol]) > Max_Y:  # Needs error checking
               Max_Y = float(TempFields[ListCol])
      
   if SkippedRows > 0:
      RtnMssg = "Skipped Rows: %d " % SkippedRows

   return(Min_Y, Max_Y, RtnMssg)
# *************** End of fcn find_y_range ************
   
def set_replaceable_values(FullPlotPath, TemplateValues, ReplaceValues):
   """ Substitute values from this session into a new template file. """

   NewTemplFile = TemplateValues

   # This group is common to all measurement types.
   NewTemplFile = NewTemplFile.replace("<PlotTitle>",
                                        ReplaceValues['PlotTitle'])

   NewXLabel = ReplaceValues['X1Label'] + '\\nStart: ' + \
               ReplaceValues['PlotStart'] + \
               "   End: " + ReplaceValues['PlotEnd']
   NewTemplFile = NewTemplFile.replace("<X1Label>", NewXLabel)

   NewTemplFile = NewTemplFile.replace("<Y1Label>", ReplaceValues['Y1Label'])

   # Add a little room between the Y1 min and max values bottom and
   # top graph border.
   GraphMinOffset = abs(ReplaceValues['Y1MinVal']) * .1
   GraphMaxOffset = ReplaceValues['Y1MaxVal'] * .1
   NewTemplFile = NewTemplFile.replace("<Y1RangeStart>",
                                       str(int(ReplaceValues['Y1MinVal']) - \
                                       GraphMinOffset))
   NewTemplFile = NewTemplFile.replace("<Y1RangeEnd>",
                                       str(int(ReplaceValues['Y1MaxVal']) + \
                                       GraphMaxOffset))

   FullPNGPath = os.path.join(FullPlotPath,
                              ReplaceValues['OutputGraphName'] + '.png')
   NewTemplFile = NewTemplFile.replace("<output.png>", FullPNGPath)

   NewTemplFile = NewTemplFile.replace("<CurrentPath>",
                                       ReplaceValues['PlotPath'])
   NewTemplFile = NewTemplFile.replace("<StatsLogFile>",
                                       ReplaceValues['LogFileName'])

   NewTemplFile = NewTemplFile.replace("<PlotStart>",
                                       ReplaceValues['PlotStart'])
   NewTemplFile = NewTemplFile.replace("<PlotEnd>",
                                       ReplaceValues['PlotEnd'])

   NewTemplFile = NewTemplFile.replace("<Plot1Color>",
                                       ReplaceValues['Plot1Color'])

   NewTemplFile = NewTemplFile.replace("<Plot1Cols>",
                                       ReplaceValues['Plot1Cols'])

   NewTemplFile = NewTemplFile.replace("<Key1Title>",
                                       ReplaceValues['Key1Title'])

   # Now deal with temperature specific values.
   if ReplaceValues['PlotType'] == "Temp":
      NewTemplFile = NewTemplFile.replace("<Y2Label>",
                                          ReplaceValues['Y2Label'])

      # Add a little room between the Y2 min and max values bottom and
      # top graph border.
      GraphMinOffset = abs(ReplaceValues['Y2MinVal']) * .1
      GraphMaxOffset = ReplaceValues['Y2MaxVal'] * .1
      NewTemplFile = NewTemplFile.replace("<Y2RangeStart>",
                                          str(int(ReplaceValues['Y2MinVal']) - \
                                          GraphMinOffset))
      NewTemplFile = NewTemplFile.replace("<Y2RangeEnd>",
                                          str(int(ReplaceValues['Y2MaxVal']) + \
                                          GraphMaxOffset))

      NewTemplFile = NewTemplFile.replace("<Plot2Color>",
                                          ReplaceValues['Plot2Color'])
      NewTemplFile = NewTemplFile.replace("<Plot2Cols>",
                                          ReplaceValues['Plot2Cols'])
      NewTemplFile = NewTemplFile.replace("<Key2Title>",
                                          ReplaceValues['Key2Title'])

   return NewTemplFile
# *************** End of fcn set_replaceable_values ************

def create_cmd_file(PlotPath, NewTemplateValues, SessionTemplateName):
   """ Create the gnuPlot command file """

   RtnMssg = ""
   RtnValue = (False, '')
   CmdPath = os.path.join(PlotPath, SessionTemplateName)

   try:
      fout = open(CmdPath, "w")
      fout.write(NewTemplateValues)
      fout.flush()    
      fout.close()  
      RtnValue = (True, "")
   except IOError, ErrMssg:
      RtnMssg = "Cannot create file %s; %s" % (CmdPath, ErrMssg)
      RtnValue = (False, RtnMssg) 

   return RtnValue
# *************** End of fcn create_plot_file ************

def create_plot_graph(PlotPath, SessionCmdFile):
   """ Run gnuPlot and create the plot graph file. """
   
   CmdPath = os.path.join(PlotPath, SessionCmdFile)
   RtnValue = (False, '')
   CmdStr = ["gnuplot", CmdPath]
   StdOutData = ""

   try:
      PlotSubProc = subprocess.Popen(CmdStr, stdout=subprocess.PIPE,
                                     stderr=subprocess.STDOUT)

      StdOutData = PlotSubProc.stdout.read()
      if len(StdOutData) == 0:
         RtnValue = (True, "")
      else:
         RtnValue = (False, StdOutData)

   except OSError, err:
      FailFlag = True
      Mssg = "Cannot start gnuPlot process\n\nSubprocess error: %s" % err
      RtnValue = (False, Mssg)
   except:
      FailFlag = True
      Mssg = "Unexpected subprocess start error: ", sys.exc_info()[0]
      RtnValue = (False, Mssg)
   
   return RtnValue
# *************** End of fcn create_plot_graph ************

def open_image_viewer(PlotPath, ViewerAppl, PlotFile):
   """ Run requested image viewer with the graphic file as a command line parameter. """

   CmdPath = os.path.join(PlotPath, PlotFile)
   RtnValue = (False, '')
   CmdStr = [ViewerAppl, CmdPath]
   StdOutData = ""

   try:
      ImgSubProc = subprocess.Popen(CmdStr, stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)

      StdOutData = ImgSubProc.stdout.read()
      if len(StdOutData) == 0:
         RtnValue = (True, "")
      else:
         RtnValue = (False, StdOutData)

   except OSError, err:
      FailFlag = True
      Mssg = "Cannot start %s process\n\nSubprocess error: %s" % \
             (ViewerAppl, err)
      RtnValue = (False, Mssg)
   except:
      FailFlag = True
      Mssg = "Unexpected %s subprocess start error: %s" % \
             (ViewerAppl, sys.exc_info()[0])
      RtnValue = (False, Mssg)
   
   return RtnValue
# *************** End of fcn open_image_viewer ************

